package com.pkk.spring.cloud.starter.rpc.common;

import com.pkk.spring.cloud.service.rpc.response.ResponseBody;
import com.pkk.spring.cloud.service.rpc.result.R;
import com.pkk.spring.cloud.starter.rpc.annotation.EnableGlobalResponse;
import com.pkk.spring.cloud.starter.rpc.annotation.IgnoreResponseConverter;
import java.util.Objects;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 普通响应类统一处理
 *
 * @author peikunkun
 * @version V1.0
 * @date 2021-01-06 16:26
 **/
//这里尽量让加密的判断优先级更低一点(请求的时候,加密的优先级高一点)
@Order(1)
@RestControllerAdvice
//当开启此注解的时候启用此响应处理器
@ConditionalOnBean(annotation = EnableGlobalResponse.class)
public class ResponseHandle implements ResponseBodyAdvice<Object> {


  /**
   * 是否支持此消息响应处理器
   *
   * @return boolean
   * @Param methodParameter
   * @Param aClass
   * @author peikunkun
   * @date 2021/1/6 0006 下午 4:29
   * @since
   */
  @Override
  public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
    //当前方法的类上存在或者方法上存在此注解,取消给解析器
    if (Objects.requireNonNull(methodParameter.getMethod()).getDeclaringClass().isAnnotationPresent(IgnoreResponseConverter.class) ||
        methodParameter.hasMethodAnnotation(IgnoreResponseConverter.class)) {
      return false;
    }
    return true;
  }

  /**
   * 在选择HttpMessageConverter之后且在调用其write方法之前调用。
   * <p>
   * 参数：正文–要写的正文
   * returnType –控制器方法的返回类型
   * selectedContentType –通过内容协商选择的内容类型
   * selectedConverterType –选择要写入响应的转换器类型
   * 请求–当前请求
   * 响应–当前响应
   * 返回值：
   * 传入的正文或经过修改的（可能是新的）实例
   */
  @Override
  public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
      Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
      ServerHttpResponse serverHttpResponse) {

    /**
     * Spring默认的String消息处理器会优先级较高于此处理器(优先处理并返回),假如我们返回的是String字符串这里我们又封装了为@ResponseBody类型的,会导致转换异常
     * 解决方案如下：
     * 1. 我们判断如果是String,则将其转换为ResponseBody类型然后再转为String字符串
     *    if(o instanceof String){
     *       return new ObjectMapper().writeValueAsString(R.success(o));
     *     }
     * 2. 我们也可以将此jackson消息处理器优先于Spring的String处理器处理
     *    @Configuration
     *    public class WebConfiguration implements WebMvcConfigurer {
     *
     *     @Override
     *     public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
     *     	  // 第一种方式是将 json 处理的转换器放到第一位，使得先让 json 转换器处理返回值，这样 String转换器就处理不了了。
     *        //        converters.add(0, new MappingJackson2HttpMessageConverter());
     * 		    // 第二种就是把String类型的转换器去掉，不使用String类型的转换器
     *        //        converters.removeIf(httpMessageConverter -> httpMessageConverter.getClass() == StringHttpMessageConverter.class);
     *      }
     *   }
     */

    ResponseBody result = null;
    if (o instanceof ResponseBody) {
      result = (ResponseBody) o;
    } else {
      result = R.success(o);
    }
    return result;
  }
}
